package com.ayz.gateway.center.infrastructure.repository;

import cn.hutool.log.Log;

import com.ayz.gateway.center.domain.model.GatewayServiceLiveInfo;
import com.ayz.gateway.center.domain.repository.IGatewayServiceRepository;
import com.ayz.gateway.center.infrastructure.notify.GatewayServiceChangeEvent;
import com.ayz.gateway.center.infrastructure.notify.GatewayServiceLogoutEvent;
import com.ayz.gateway.center.infrastructure.notify.GatewayServiceSubscriber;
import com.ayz.gateway.common.constants.GatewayServiceData;
import com.ayz.gateway.common.notify.NotifyCenter;
import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @ClassName LocalGatewayServiceRepository
 * @Description TODO: 网关服务的存储中心
 * @Author 快乐的星球
 * @Date 2023/5/25 15:14
 * @Version 1.0
 **/
@Getter
@Setter
@Component
public class LocalGatewayServiceRepository implements IGatewayServiceRepository {

    private final Log LOG=Log.get(LocalGatewayServiceRepository.class);
    /**
     * key:网关服务的唯一标识Id
     * value: 网关服务的基础信息
     */
    private final HashMap<String, GatewayServiceData> gatewayServiceMap=new HashMap<>(128);
    /**
     * key: 网关业务组的名称
     * value: List<gatewayId> 网关服务的唯一标识Id
     */
    private final HashMap<String, HashSet<String>> gatewayGroupMap=new HashMap<>();

    /**
     * key: 网关服务的唯一标识ID
     * value: 网关服务的心跳信息类
     */
    private final HashMap<String, GatewayServiceLiveInfo> gatewayServiceLiveInfoMap=
            new HashMap<>(128);

    /**
     * key: 网关服务的唯一标识ID
     * value: 网关服务的订阅者
     */
    private final ConcurrentHashMap<String, GatewayServiceSubscriber> gatewayServiceSubscriberMap=
            new ConcurrentHashMap<>(128);
    /**
     * 读写锁
     */
    private final ReentrantReadWriteLock readWriteLock=new ReentrantReadWriteLock();


    /**
     * 网关服务每隔10S钟会向网关注册中心拉取网关组的配置信息
     * 顺便会使用当前方法进行心跳
     * @param gatewayServiceData 网关服务的基础信息类
     */
    @Override
    public void registryGatewayService(GatewayServiceData gatewayServiceData) {
        if(gatewayServiceData==null){
            return;
        }
        LOG.info("网关服务注册;gatewayId:{};gatewayName:{};gatewayGroup:{},address:{}",
                gatewayServiceData.getGatewayId(),gatewayServiceData.getGatewayName(),
                gatewayServiceData.getGatewayGroup(),gatewayServiceData.getAddress());
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        try {
            /**
             * 维护三个Map结构
             *
             */
            //1:维护gatewayServiceMap结构
            String gatewayId = gatewayServiceData.getGatewayId();
            this.gatewayServiceMap.put(gatewayId,gatewayServiceData);
            //2:维护gatewayGroupMap结构
            String gatewayGroup = gatewayServiceData.getGatewayGroup();
            HashSet<String> gatewayServiceSet = this.getGatewayGroupMap().get(gatewayGroup);
            if(gatewayServiceSet==null){
                gatewayServiceSet=new HashSet<>();
                this.gatewayGroupMap.put(gatewayGroup,gatewayServiceSet);
            }
            gatewayServiceSet.add(gatewayId);
            //3:维护gatewayServiceLiveInfoMap结构
            String gatewayName=gatewayServiceData.getGatewayName();
            GatewayServiceLiveInfo gatewayServiceLiveInfo = this.gatewayServiceLiveInfoMap.get(gatewayId);
            if(gatewayServiceLiveInfo==null){
                //第一次注册,发布网关服务变更事件
                GatewayServiceChangeEvent event=
                        new GatewayServiceChangeEvent(new GatewayServiceChangeEvent.GatewayServiceMetadata(true,gatewayServiceData,this));
                NotifyCenter.getINSTANCE().publishApplicationEvent(event);
                gatewayServiceLiveInfo=new GatewayServiceLiveInfo(gatewayName,gatewayGroup,gatewayId);
                this.getGatewayServiceLiveInfoMap().put(gatewayId,gatewayServiceLiveInfo);
            }else{
                gatewayServiceLiveInfo.setLastHeartBeatTimestamp(System.currentTimeMillis());
            }
        }finally {
            if(writeLock.isHeldByCurrentThread()){
                writeLock.unlock();
            }
        }
    }

    @Override
    public void logoutGatewayService(String gatewayId) {
        LOG.info("网关服务注销!gatewayId:{}",gatewayId);
        GatewayServiceData gatewayServiceData = this.getGatewayServiceMap().get(gatewayId);
        if(gatewayServiceData==null){
            LOG.info("当前网关服务已经注销过了!gatewayId:{}",gatewayId);
            return;
        }
        String gatewayGroup = gatewayServiceData.getGatewayGroup();
        ReentrantReadWriteLock.WriteLock writeLock = readWriteLock.writeLock();
        writeLock.lock();
        //double check 双重检查加写锁
        gatewayServiceData = this.getGatewayServiceMap().get(gatewayId);
        if(gatewayServiceData==null){
            LOG.info("当前网关服务已经注销过了!gatewayId:{}",gatewayId);
            return;
        }
        try {
            //TODO: 发布网关服务注销事件
            GatewayServiceChangeEvent event=new GatewayServiceChangeEvent(
                    new GatewayServiceChangeEvent.GatewayServiceMetadata(false,gatewayServiceData,this)
            );
            NotifyCenter.getINSTANCE().publishApplicationEvent(event);
            //1:维护gatewayServiceMap结构
            this.getGatewayServiceMap().remove(gatewayId);
            //2:维护gatewayGroupMap结构
            HashSet<String> gatewayServiceSet = this.gatewayGroupMap.get(gatewayGroup);
            gatewayServiceSet.remove(gatewayId);
            if(gatewayServiceSet.isEmpty()){
                this.gatewayGroupMap.remove(gatewayGroup);
            }
            //3:维护gatewayServiceLiveInfoMap结构
            this.getGatewayServiceLiveInfoMap().remove(gatewayId);
        }finally {
            if(writeLock.isHeldByCurrentThread()){
                writeLock.unlock();
            }
        }
    }

    @Override
    public List<GatewayServiceData> getGatewayServiceListByGroup(String gatewayGroup) {
        HashMap<String, HashSet<String>> gatewayGroupMap = this.gatewayGroupMap;
        //TODO: 获取读锁
        ReentrantReadWriteLock.ReadLock readLock = this.readWriteLock.readLock();
        readLock.lock();
        try {
            HashSet<String> gatewayServiceSet = gatewayGroupMap.get(gatewayGroup);
            if(gatewayServiceSet==null || gatewayServiceSet.isEmpty()){
                return new ArrayList<>();
            }
            //当前网关组不为空
            List<GatewayServiceData> gatewayServiceDataList=new ArrayList<>();
            gatewayServiceSet.forEach((gatewayId)->{
                GatewayServiceData gatewayServiceData = this.gatewayServiceMap.get(gatewayId);
                gatewayServiceDataList.add(gatewayServiceData);
            });
            return gatewayServiceDataList;
        }finally {
            readLock.unlock();
        }
    }

    @Override
    public void addGatewayServiceSubscriber(String gatewayId, GatewayServiceSubscriber serviceSubscriber) {
        this.gatewayServiceSubscriberMap.put(gatewayId,serviceSubscriber);
    }

    @Override
    public boolean containsGatewayServiceSubscriber(String gatewayId) {
        return this.gatewayServiceSubscriberMap.containsKey(gatewayId);
    }

    @Override
    public void removeGatewayServiceSubscriber(String gatewayId) {
        this.gatewayServiceSubscriberMap.remove(gatewayId);
    }

    @Override
    public GatewayServiceData getGatewayServiceData(String gatewayId) {
        //TODO:获取读锁
        ReentrantReadWriteLock.ReadLock readLock = this.readWriteLock.readLock();
        readLock.lock();
        try {
            return this.gatewayServiceMap.get(gatewayId);
        }finally {
            readLock.unlock();
        }
    }

    /**
     * 网关注册中心/配置 心跳检查 定时清除超过指定心跳时间的节点
     * @param gatewayServiceChannelExpiredTime
     */
    @Override
    public void scanNotActiveGatewayService(long gatewayServiceChannelExpiredTime) {
        //存储所有超过指定时间没有心跳的不活跃节点
        List<String> unActiveNode=new ArrayList<>();
        this.gatewayServiceLiveInfoMap.forEach((gatewayId,liveInfo)->{
            long lastHeartBeatTimestamp = liveInfo.getLastHeartBeatTimestamp();
            if(System.currentTimeMillis()-lastHeartBeatTimestamp>gatewayServiceChannelExpiredTime){
                unActiveNode.add(gatewayId);
            }
        });
        //TODO:获取写锁
        ReentrantReadWriteLock.WriteLock writeLock = this.readWriteLock.writeLock();
        writeLock.lock();
        try {
            //TODO:完成所有不活跃节点的剔除
            unActiveNode.forEach((gatewayId)->{
                LOG.info("网关唯一标识符 gatewayId:{} 的节点心跳超时!",gatewayId);
                //1:维护gatewayServiceMap结构
                GatewayServiceData gatewayServiceData = this.gatewayServiceMap.get(gatewayId);

                //TODO:发布网关服务变更事件
                GatewayServiceChangeEvent event=
                        new GatewayServiceChangeEvent(new GatewayServiceChangeEvent.GatewayServiceMetadata(false,gatewayServiceData,this));
                NotifyCenter.getINSTANCE().publishApplicationEvent(event);

                String gatewayGroup = gatewayServiceData.getGatewayGroup();

                this.gatewayServiceMap.remove(gatewayId);
                //2:维护gatewayGroupMap结构
                HashSet<String> gatewaySet = this.gatewayGroupMap.get(gatewayGroup);
                gatewaySet.remove(gatewayId);
                if(gatewaySet.isEmpty()){
                    this.gatewayGroupMap.remove(gatewayGroup);
                }
                //3:维护gatewayServiceLiveInfoMap结构
                this.gatewayServiceLiveInfoMap.remove(gatewayId);
                //4:维护gatewayServiceSubscriberMap结构
                GatewayServiceSubscriber gatewayServiceSubscriber = this.gatewayServiceSubscriberMap.get(gatewayId);
                if(gatewayServiceSubscriber!=null){
                    this.gatewayServiceSubscriberMap.remove(gatewayId);
                    //1:发布网关服务注销时间
                    GatewayServiceLogoutEvent gatewayServiceLogoutEvent=new GatewayServiceLogoutEvent(gatewayId);
                    NotifyCenter.getINSTANCE().publishApplicationEvent(gatewayServiceLogoutEvent);
                    //2:通知中心移除监听器
                    NotifyCenter.getINSTANCE().getEventMulticaster().removeApplicationListener(gatewayServiceSubscriber);
                }
            });
        }finally {
            if(writeLock.isHeldByCurrentThread()){
                writeLock.unlock();
            }
        }
    }
}
